[PB Generic表][Go SDK]批量部分字段查询

1. 接口说明

BatchFieldGet,从数据库表中批量查询多条记录的部分字段。

注意:

  • 最多批量操作1024条记录;
  • 每条请求记录需要填写完整主键字段,返回字段通过 option.PBOpt.FieldNames 指定;
  • FieldNames 只能填写 value 字段,不能填写 key 字段;嵌套字段使用点号连接,例如 pay.amount
  • 单条记录的错误存放在 option.PBOpt.BatchResult 中,单条记录的版本号存放在 option.PBOpt.BatchVersion 中;
  • 调用时要设置 MultiFlag 为 1,允许响应分包,避免响应包超过256KB后不分包导致丢包。

更多说明参见批量操作接口的相关说明

2. 版本要求

3.55.0以上版本SDK。

3. 准备工作

参见准备工作文档,完成使用该接口前的准备工作,并创建如下PB Generic表。game_players表

syntax = "proto3";                      // Specify the version of the protocol buffers language

package tcaplusservice;

import "tcaplusservice.optionv1.proto"; // Use the public definitions of TcaplusDB by importing them.

message game_players {  // Define a TcaplusDB table with message

// Specify the primary keys with the option tcaplusservice.tcaplus_primary_key
// The primary key of a TcaplusDB table has a limit of 4 fields
option(tcaplusservice.tcaplus_primary_key) = "player_id, player_name, player_email";

// Specify the primary key indexes with the option tcaplusservice.tcaplus_index
option(tcaplusservice.tcaplus_index) = "index_1(player_id, player_name)";
option(tcaplusservice.tcaplus_index) = "index_2(player_id, player_email)";


// Value Types supported by TcaplusDB
// int32, int64, uint32, uint64, sint32, sint64, bool, fixed64, sfixed64, double, fixed32, sfixed32, float, string, bytes
// Nested Types Message

// primary key fields
int64 player_id = 1;
string player_name = 2;
string player_email = 3;


// Ordinary fields
int32 game_server_id = 4;
repeated string login_timestamp = 5;
repeated string logout_timestamp = 6;
bool is_online = 7;

payment pay = 8;
}


message payment {

int64 pay_id = 1;
uint64 amount = 2;
int64 method = 3;

}

准备工作完成后,将会获得以下信息,这些信息在使用SDK时会被用到:

  1. 目录服务器地址列表
  2. 业务ID
  3. 业务访问密码
  4. 游戏区ID
  5. 数据表名

4. 示例代码

示例代码的基本执行过程:

  1. 定义表配置参数
  2. 设置日志配置;
  3. 创建客户端;
  4. 发送请求并处理响应;
  5. 销毁客户端。

客户端初始化示例:

package main

import (
    "fmt"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api"
)

// 定义表配置参数
const (
    AppId     = uint64(2)
    ZoneId    = uint32(3)
    DirUrl    = "tcp://x.x.x.x:xxxx"
    Signature = "xxxxxxxxxxxxx"
    TableName = "game_players"
)

var client *tcaplus.PBClient

func main() {
    // 创建客户端
    client = tcaplus.NewPBClient()
    // 设置日志配置,logconf.xml文件设置了日志级别
    if err := client.SetLogCfg("./logconf.xml"); err != nil {
        fmt.Println(err.Error())
        return
    }

    // 构造Map对象存储对应表格组下所有的表
    zoneList := []uint32{ZoneId}
    zoneTable := make(map[uint32][]string)
    zoneTable[ZoneId] = []string{TableName}

    // 连接TcaplusDB后端
    err := client.Dial(AppId, zoneList, DirUrl, Signature, 30, zoneTable)
    if err != nil {
        fmt.Printf("init failed %v\n", err.Error())
        return
    }
    // 设置默认使用的zone
    client.SetDefaultZoneId(ZoneId)
    fmt.Printf("Dial finish\n")
    PbBatchFieldGetExample()

    // 程序退出时调用Close销毁客户端
    client.Close()
}

4.1 同步调用示例(推荐)

同步调用编码最简单,通过多协程并发。 示例目录

package main

import (
    "fmt"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/table/tcaplusservice"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/tools"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api/logger"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api/protocol/option"
    "google.golang.org/protobuf/proto"
)

func PbBatchFieldGetExample() {
    msg1 := &tcaplusservice.GamePlayers{
        PlayerId:    10805514,
        PlayerName:  "Calvin",
        PlayerEmail: "calvin@test.com",
    }

    msg2 := &tcaplusservice.GamePlayers{
        PlayerId:    10805515,
        PlayerName:  "Calvin",
        PlayerEmail: "calvin@test.com",
    }

    msg3 := &tcaplusservice.GamePlayers{
        PlayerId:    108055150000,
        PlayerName:  "Calvin",
        PlayerEmail: "calvin@test.com",
    }

    var msgs []proto.Message
    msgs = append(msgs, msg1)
    msgs = append(msgs, msg2)
    msgs = append(msgs, msg3)

    // 设置允许响应分包,并指定本次只查询 game_server_id 和二级字段 pay.amount
    opt := &option.PBOpt{
        MultiFlag:  1,
        FieldNames: []string{"game_server_id", "pay.amount"},
    }
    err := client.DoBatchFieldGet(msgs, opt)
    if err != nil {
        logger.ERR("DoBatchFieldGet error:%s", err)
        return
    }

    for i, rspMsg := range msgs {
        fmt.Println(tools.ConvertToJson(rspMsg))
        fmt.Println(rspMsg.(*tcaplusservice.GamePlayers))
        // 单条记录的错误码
        fmt.Println("result", opt.BatchResult[i])
        // 记录version
        fmt.Println("version", opt.BatchVersion[i])
    }
    logger.INFO("batch field get success")
    fmt.Println("batch field get success")
}

4.2 同步调用示例2(推荐)

该示例同步调用方式比较类似C++接口的调用方式,需要创建请求和解析响应。 示例目录

package main

import (
    "fmt"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/table/tcaplusservice"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/tools"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api/logger"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api/protocol/cmd"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api/terror"
    "time"
)

func PbBatchFieldGetExample() {
    // 生成 batch field get 请求
    req, err := client.NewRequest(tools.Zone, "game_players", cmd.TcaplusApiPBBatchFieldGetReq)
    if err != nil {
        logger.ERR("NewRequest error:%s", err)
        return
    }
    // 设置分包
    req.SetMultiResponseFlag(1)

    fieldNames := []string{"game_server_id", "pay.amount"}

    msg1 := &tcaplusservice.GamePlayers{
        PlayerId:    10805514,
        PlayerName:  "Calvin",
        PlayerEmail: "calvin@test.com",
    }
    msg2 := &tcaplusservice.GamePlayers{
        PlayerId:    10805515,
        PlayerName:  "Calvin",
        PlayerEmail: "calvin@test.com",
    }

    for _, msg := range []*tcaplusservice.GamePlayers{msg1, msg2} {
        // 向请求中添加记录,对于 generic 表 index 无意义,填 0 即可
        record, err := req.AddRecord(0)
        if err != nil {
            logger.ERR("AddRecord error:%s", err)
            return
        }

        // key字段必填,通过第二个参数指定本次查询的部分value字段
        _, err = record.SetPBFieldValues(msg, fieldNames)
        if err != nil {
            logger.ERR("SetPBFieldValues error:%s", err)
            return
        }
    }

    // 发送请求,接收响应
    resps, err := client.DoMore(req, 5*time.Second)
    if err != nil {
        logger.ERR("SendRequest error:%s", err)
        return
    }

    for _, resp := range resps {
        // 获取响应结果
        errCode := resp.GetResult()
        if errCode != terror.GEN_ERR_SUC {
            logger.ERR("batch field get error:%s", terror.GetErrMsg(errCode))
            return
        }

        // 如果有返回记录则用以下接口进行获取
        for i := 0; i < resp.GetRecordCount(); i++ {
            record, err := resp.FetchRecord()
            if err != nil {
                logger.ERR("FetchRecord failed %s", err.Error())
                return
            }

            newMsg := &tcaplusservice.GamePlayers{}
            err = record.GetPBFieldValues(newMsg)
            if err != nil {
                logger.ERR("GetPBFieldValues failed %s", err.Error())
                return
            }

            fmt.Println(tools.ConvertToJson(newMsg))
        }
    }

    logger.INFO("batch field get success")
    fmt.Println("batch field get success")
}

5. PBClient中的方法说明

/**
  @brief  批量查询Generic表多条记录的部分value字段
  @param [IN/OUT] msgs   由proto文件生成的记录结构体,入参需要填写完整主键字段;若查询成功,会更新为返回的记录
  @param [IN/OUT] opt    可选参数。FieldNames必填;若有记录返回,会更新BatchResult和BatchVersion
  @param [IN] zoneId     可选参数,不设置则取默认zone,默认zone可通过client.SetDefaultZoneId设置
  @retval error          错误码
*/
func (c *PBClient) DoBatchFieldGet(msgs []proto.Message, opt *option.PBOpt, zoneId ...uint32) error

6. PBOpt中的相关参数说明

type PBOpt struct {
    // 批量操作中每条记录的版本号,调用DoBatchFieldGet后填充
    BatchVersion []int32

    // 批量操作中每条记录的操作结果,调用DoBatchFieldGet后填充
    BatchResult []error

    // 如果此请求会返回多条记录,设置是否允许一个请求包自动响应多个应答包
    // 1表示允许分包,0表示不允许分包
    MultiFlag byte

    // 超时时间,不设置默认使用客户端默认超时时间
    Timeout time.Duration

    // Go协程context,不设置默认使用Timeout,否则使用context判断协程退出
    Ctx context.Context

    // PB FieldGet和FieldSet使用,获取或更新部分字段;DoBatchFieldGet必填
    FieldNames []string
}

7. 响应对象(response)中的方法说明

注:此处未列出的响应对象的其它方法,即表示该方法在批量部分字段查询的场景不需要使用,误用可能会导致报错

/*
  @brief  获取响应消息的结果
  @retval 0 成功,其它值失败
*/
GetResult() int

/*
  @brief  获取本响应中结果记录条数
  @retval int 响应中结果记录条数
*/
GetRecordCount() int

/*
  @brief  从结果中获取一条记录
  @retval *record.Record 记录指针
  @retval error 错误码
*/
FetchRecord() (*record.Record, error)

/*
  @判断是否有更多的回包
  @retval 1 有,0 没有
*/
HaveMoreResPkgs() int

8. 记录对象(record)中的方法说明

注:此处未列出的记录对象的其它方法,即表示该方法在批量部分字段查询的场景不需要使用,误用可能会导致报错

/**
  @brief  获取记录版本号
  @retval 记录版本号
**/
func (r *Record) GetVersion() int32

/**
  @brief 设置部分value字段,专用于field操作,TcaplusApiPBFieldGetReq、TcaplusApiPBFieldUpdateReq、TcaplusApiPBFieldIncreaseReq、TcaplusApiPBBatchFieldGetReq
  @param [IN] message proto.Message 由proto文件生成的记录结构体
  @param [IN] values []string 指定本次设置的value字段
  @retval []byte 由记录key字段编码生成,由于多条记录的响应记录是无序的,可以用这个值来匹配记录
  @retval error 错误码
**/
func (r *Record) SetPBFieldValues(message proto.Message, values []string) ([]byte, error)

/**
  @brief 基于PB Message读取field操作返回的部分字段
  @param [OUT] message proto.Message 由proto文件生成的记录结构体
  @retval error 错误码
**/
func (r *Record) GetPBFieldValues(message proto.Message) error

results matching ""

    No results matching ""